package com.loong.framework.aspect;

import com.loong.framework.annotation.RateLimiter;
import com.loong.framework.constant.SystemConst;
import com.loong.framework.utils.RedisUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class RateLimiterAspect {

  @Resource private RedisUtil redisUtil;
  @Resource private HttpServletRequest request;

  @Pointcut("@annotation(rateLimiter)")
  public void rateLimitPointcut(RateLimiter rateLimiter) {}

  @Before(value = "rateLimitPointcut(rateLimiter)", argNames = "joinPoint, rateLimiter")
  public void before(JoinPoint joinPoint, RateLimiter rateLimiter) {
    String key = rateLimiter.key();
    long time = rateLimiter.time();
    int count = rateLimiter.count();
    String limitMsg = rateLimiter.limitMsg();

    // 基于 IP 的限流逻辑
    handleIpLimiting(key, time, count, limitMsg);
  }

  // 处理基于 IP 的限流逻辑
  private void handleIpLimiting(String key, long time, int count, String limitMsg) {
    // 获取请求的 IP 地址
    String ipAddress = getIpAddress();

    // 构造 Redis Key
    String redisKey = SystemConst.LimitKey + key + ":" + ipAddress;

    // 删除超过时间窗口的旧数据
    redisUtil.zRemRangeByScore(
        redisKey, 0, System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(time));

    // 获取当前时间窗口内的请求数量
    long currentCount = redisUtil.zCard(redisKey);

    // 如果超过限流次数，则拒绝请求
    if (currentCount >= count) {
      throw new RuntimeException(limitMsg);
    }

    // 否则，将当前时间戳写入 Redis
    redisUtil.zAdd(
        redisKey, System.currentTimeMillis(), String.valueOf(System.currentTimeMillis()));

    // 设置过期时间
    redisUtil.expire(redisKey, time, TimeUnit.SECONDS);
  }

  // 获取请求的 IP 地址
  private String getIpAddress() {
    // 这里假设通过 HttpServletRequest 获取 IP 地址
    return request.getRemoteAddr();
  }
}
